﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IURIP;
using System.Xml;
using URIP.Tool;

namespace URIP.Core
{
    public class Map : IMap
    {
        public Map(ITarget target)
        {
            m_target = target;
        }

        private ITarget m_target = null;
        private List<IMapItem> m_mapItems = new List<IMapItem>();
        private Dictionary<string, IMapItem> m_mapItemsDic = new Dictionary<string, IMapItem>();
        private Dictionary<string, List<IMapItem>> m_mapItemsGroup = new Dictionary<string, List<IMapItem>>();
        private XmlNode m_mapTemplate = null;
        private List<string> m_dataBlocks = new List<string>();

        public ITask Task
        {
            get { return m_target.Task; }
        }
        public ITarget Target
        {
            get { return m_target; }
        }
        public List<IMapItem> MapItems
        {
            get { return m_mapItems; }
        }
        public List<string> DataBlocks
        {
            get
            {
                return m_dataBlocks;
            }
        }

        public void InitByConfiguration(XmlNode node)
        {
            m_mapTemplate = Util.GetXmlNode(node.OuterXml);
            XmlNodeList childrenFieldList = node.SelectNodes("./field");
            this.InitChildren(childrenFieldList);
            XmlNodeList childrenListList = node.SelectNodes("./list");
            this.InitChildren(childrenListList);
            foreach (var kvp in Target.DataBlocks)
            {
                if (!m_dataBlocks.Contains(kvp.Key))
                {
                    Task.Logger.LogWarning("The data block {0} is not used in map for target {1}", kvp.Key, Target.Name);
                }
            }
        }
        private void InitChildren(XmlNodeList childrenList)
        {
            for (int i = 0; i < childrenList.Count; i++)
            {
                IMapItem item = new MapItem(this, null);
                item.InitByConfiguration(childrenList[i]);
                if (m_mapItemsDic.ContainsKey(item.Name))
                {
                    throw new Exception(string.Format("The map item name '{0}' for target '{1}' is repeated, it should be renamed", item.Name, Target.Name));
                }
                else
                {
                    m_mapItems.Add(item);
                    m_mapItemsDic.Add(item.Name, item);
                    if (!m_mapItemsGroup.ContainsKey(item.DataBlockName))
                    {
                        m_mapItemsGroup[item.DataBlockName] = new List<IMapItem>();
                    }
                    m_mapItemsGroup[item.DataBlockName].Add(item);
                }
            }
        }
        public IMapItem GetMapItemByName(string name)
        {
            if (m_mapItemsDic.ContainsKey(name))
            {
                return m_mapItemsDic[name];
            }
            return null;
        }
        public IMapItem GetMapItemByXPath(string xpath)
        {
            IMapItem res = null;
            XmlNode node = m_mapTemplate.SelectSingleNode(xpath);
            LinkedList<string> nameList = this.GetMapItemFullPathNameList(node);
            LinkedListNode<string> name = nameList.First;
            if (name != null)
            {
                res = GetMapItemByName(name.Value);
                while (name.Next != null)
                {
                    name = name.Next;
                    res = res.GetMapItemByName(name.Value);
                }
            }
            return res;
        }
        public List<IMapItem> GetListByBlockName(string blockName)
        {
            if (m_mapItemsGroup.ContainsKey(blockName))
            {
                return m_mapItemsGroup[blockName];
            }
            return new List<IMapItem>();
        }
        public List<IMapItem> GetListByBlockNameAndXPath(string blockName, string path)
        {
            IMapItem tmp = this.GetMapItemByXPath(path);
            return tmp == null ? this.GetListByBlockName(blockName) : tmp.GetListByBlockName(blockName);
        }

        private LinkedList<string> GetMapItemFullPathNameList(XmlNode mapItemNode)
        {
            LinkedList<string> res = new LinkedList<string>();
            while (mapItemNode != null)
            {
                string name = Util.GetAttributeValue(mapItemNode, "name");
                if (name != "")
                {
                    res.AddFirst(name);
                }
                mapItemNode = mapItemNode.ParentNode;
            }
            return res;
        }
    }
}
